package array;

/**
 * A class containing common operations performed on arrays.
 * 
 * @author Shimu
 * @date 28-Sep-2012
 */
public class ArrayFunctions {

    // ======================= Common Array Operations =======================
    /**
     * Given an array containing elements of an arbitrary type T, replaces
     * the element at the given index with the given newElement. Returns the
     * previous element at the given index.
     * 
     * @param <T> the type of the elements in the array
     * @param array 
     * @param index 
     * @param newElement
     * @throws IndexOutOfBoundsException if the given index is outside the
     * bounds of this array
     * @return the previous element at the given index, null if no element
     * existed at the given index.
     */
    public static <T> T replace(T[] array, int index, T newElement) {
        T previous = array[index];
        array[index] = newElement;
        return previous;
    }

    /**
     * Swap the elements at index i and j.
     * @param <T>
     * @param array
     * @param i
     * @param j 
     */
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    // ======================= Resizing Arrays =======================
    /**
     * Returns a new array that contains the same content as the given
     * array, except its capacity is scaled by the given scaleFactor.
     * If the expansionFactor &lt; 1 and the new array does not have enough
     * room to store all the elements of the given array, an ArrayStoreException
     * is thrown.
     * 
     * @param <T>
     * @param array
     * @param scaleFactor
     * @return 
     */
    public static <T> T[] changeCapacity(T[] array, double scaleFactor) {
        int newSize = (int) (array.length * scaleFactor);
        T[] newArray = (T[]) new Object[newSize];
        arraycopy(array, 0, newArray, 0, array.length);
        return newArray;
    }

    // ======================= Conversion Between Arrays =======================
    /**
     * Returns a double array containing the data of the given array.
     * @param <T>
     * @param array
     * @param columnCount the number of elements in a column
     * @return 
     */
    public static <T> T[][] toDoubleArray(T[] array, int columnCount) {
        final int rowCount = (columnCount > 0) ? array.length / columnCount : 0;
        T[][] newArray = (T[][]) new Object[rowCount][columnCount];
        for (int r = 0; r < rowCount; r++) {
            arraycopy(array, r * columnCount, newArray[r], 0, columnCount);
        }
        return newArray;
    }

    // <editor-fold defaultstate="collapsed" desc="Primative Array to Wrapper Array">
    public static Float[] toWrapperArray(float[] array) {
        Float[] newArray = new Float[array.length];
        for (int i = 0; i < array.length; i++) {
            newArray[i] = new Float(array[i]);
        }
        return newArray;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Wrapper Array to Primative Array">
    public static float[] toPrimativeArray(Float[] array) {
        float[] newArray = new float[array.length];
        for (int i = 0; i < array.length; i++) {
            newArray[i] = array[i].floatValue();
        }
        return newArray;
    }

    // </editor-fold>
    // ======================= Array Creation =======================
    /**
     * Returns a new array with the given size where all the elements 
     * point to the given startElement.
     * @param <T> The type of the given startElement
     * @param startElement
     * @param size the size of the newArray
     * @return 
     */
    public static <T> T[] createArray(T startElement, int size) {
        T[] newArray = (T[]) new Object[size];
        for (int i = 0; i < size; i++) {
            newArray[i] = startElement;
        }
        return newArray;
    }

    /**
     * Returns a new array that contains the same content as the given array
     * from index startIndex to array.length. Returns an empty array if 
     * startIndex is invalid.
     * 
     * @param <T>
     * @param array
     * @param startIndex inclusive
     * @param endIndex exclusive
     * @return 
     */
    public static <T> T[] subarray(T[] array, int startIndex, int endIndex) {
        startIndex = Math.max(0, startIndex);
        endIndex = Math.min(endIndex, array.length);
        
        int length = endIndex - startIndex;
        
        T[] newArray = (T[]) new Object[length];
        System.arraycopy(array, startIndex, newArray, 0, length);
        return newArray;
    }
    
    public static <T> T[] copy(T[] array) {
        return subarray(array, 0, array.length);
    }

    // ======================= Private Helper Methods =======================
    /**
     * A wrapper method around System.arraycopy. This was done so if this
     * were to be ported, one simply needs to change this method instead
     * of all the instances where System.arraycopy is used.
     * 
     * @param src
     * @param srcStart
     * @param dest
     * @param destStart
     * @param length 
     */
    private static void arraycopy(Object src, int srcStart, Object dest,
            int destStart, int length) {
        System.arraycopy(src, srcStart, dest, destStart, length);
    }
    
    public static void reverse(Object[] array) {
        for (int i = 0; i < array.length /2; i++) {
            swap(array, i, array.length - 1 - i);
        }
    }
}
